package com.hmdp.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.hmdp.utils.RedisConstants.SIGN_KEY;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录或注册
     *
     * @param loginForm
     */
    @Override
    public User loginOrRegister(LoginFormDTO loginForm) {
        // 2. 根据手机号查询用户，如果没有则注册
        User user = userService.query().eq("phone", loginForm.getPhone()).one();
        // 不存在，则注册
        if (user == null) {
            // 注册新用户
            user = createNewUser(loginForm.getPhone());
        }
        return user;
    }

    @Override
    public Result sign() {
        // 获取当前用户
        Long userId = UserHolder.getUser().getId();
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        String nowStr = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        // 组装redis的key sign:user_id:日期
        String signKey = SIGN_KEY + userId + ":" + nowStr;
        // 偏移量offset：当天在所在月的第几天
        int offset = now.getDayOfMonth() - 1;
        stringRedisTemplate.opsForValue().setBit(signKey, offset, true);
        // 签到成功，返回成功信息
        return Result.ok();
    }

    @Override
    public Result signCount() {
        // 获取当前用户
        Long userId = UserHolder.getUser().getId();
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        String nowStr = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        // 组装redis的key sign:user_id:日期
        String signKey = SIGN_KEY + userId + ":" + nowStr;
        // 本月已经过去的天数
//        List<Long> days = stringRedisTemplate.opsForValue().bitField(signKey,
//                BitFieldSubCommands.create(BitFieldSubCommands.BitFieldGet.create(BitFieldSubCommands.BitFieldType.UINT_32, BitFieldSubCommands.Offset.offset(0L))));

        int daysInMonth = now.getDayOfMonth();
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                signKey,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(daysInMonth)).valueAt(0)
        );
        if (result == null || result.isEmpty()) {
            // 没有任何签到结果
            return Result.ok(0);
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return Result.ok(0);
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算，得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0，说明未签到，结束
                break;
            } else {
                // 如果不为0，说明已签到，计数器+1
                count++;
            }
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }
        return Result.ok(count);
    }

    /**
     * 将十进制值列表转换为二进制数组
     *
     * @param decimalList 十进制值列表
     * @return 二进制数组
     */
    private byte[] convertDecimalListToByteArray(List<Long> decimalList) {
        // 计算二进制数组的长度
        int byteArrayLength = (decimalList.size() + 7) / 8;
        byte[] byteArray = new byte[byteArrayLength];

        // 遍历十进制值列表，将每个值转换为二进制并写入二进制数组
        for (int i = 0; i < decimalList.size(); i++) {
            long value = decimalList.get(i);
            int byteIndex = i / 8;
            int bitIndex = i % 8;

            // 将十进制值转换为二进制，并将其写入对应的字节
            byteArray[byteIndex] |= (value & 1) << bitIndex;
        }

        return byteArray;
    }

    /**
     * 创建新用户
     *
     * @param phone
     * @return
     */
    private User createNewUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(6));
        userService.save(user);
        return user;
    }
}
